package thema2project;

import java.util.Random;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;

public class Simulator {
	// Constants representing configuration information for the simulation.
	// The default width for the grid.
	private static final int DEFAULT_WIDTH = 100;
	// The default depth of the grid.
	private static final int DEFAULT_DEPTH = 100;
	// The probability that a fox will be created in any given grid position.
	private static final double FOX_CREATION_PROBABILITY = 0.02;
	// The probability that a rabbit will be created in any given grid position.
	private static final double RABBIT_CREATION_PROBABILITY = 0.08;
	// The probability that a bear will be creadted in any given grid position.
	private static final double BEAR_CREATION_PROBABILITY = 0.01;
	// The probability that a hunter will be creadted in any given grid
	// position.
	private static final double HUNTER_CREATION_PROBABILITY = 0.0005;

	// List of animals in the field.
	private List<Actor> actors;
	// The current state of the field.
	private Field field;
	// The current step of the simulation.
	private int step;
	// A graphical view of the simulation.
	private SimulatorView view;
	/**
	 * Construct a simulation field with default size.
	 */
	public Simulator() {
		this(DEFAULT_WIDTH, DEFAULT_DEPTH);
	}

	public Field getField() {
		return field;
	}

	public int getStep() {
		return step;
	}

	/**
	 * Create a simulation field with the given size.
	 * 
	 * @param depth
	 *            Depth of the field. Must be greater than zero.
	 * @param width
	 *            Width of the field. Must be greater than zero.
	 */

	public Simulator(int depth, int width) {
		if (width <= 0 || depth <= 0) {
			System.out.println("The dimensions must be greater than zero.");
			System.out.println("Using default values.");
			depth = DEFAULT_DEPTH;
			width = DEFAULT_WIDTH;
		}

		actors = new ArrayList<Actor>();
		field = new Field(depth, width);
	}

	/**
	 * Run the simulation from its current state for a reasonably long period,
	 * e.g. 500 steps.
	 */
	public void runLongSimulation() {
		simulate(500);
	}

	/**
	 * Run the simulation from its current state for the given number of steps.
	 * Stop before the given number of steps if it ceases to be viable.
	 * 
	 * @param numSteps
	 *            The number of steps to run for.
	 */
	public void simulate(int numSteps) {
		for (int step = 1; step <= numSteps && view.isViable(field); step++) {
			simulateOneStep();
		}
	}

	/**
	 * Run the simulation from its current state for a single step. Iterate over
	 * the whole field updating the state of each fox and rabbit.
	 */
	public void simulateOneStep() {
		step++;

		// Provide space for newborn animals.
		List<Actor> newActors = new ArrayList<Actor>();
		// Let all rabbits act.
		for (Iterator<Actor> it = actors.iterator(); it.hasNext();) {
			Actor actor = it.next();
			actor.act(newActors);
			if (!actor.isAlive()) {
				it.remove();
			}
		}
		// Add the newly born foxes and rabbits to the main lists.
		actors.addAll(newActors);


		view.showStatus(step, field);
		view.hText(step, field);
		view.repaint();
	}

	public SimulatorView getView() {
		return view;
	}

	public void setView(SimulatorView view) {
		this.view = view;
	}

	/**
	 * Reset the simulation to a starting position.
	 */
	public void reset() {
		step = 0;
		actors.clear();
		view.clearTextArea();
		populate();

		// Show the starting state in the view.
		view.showStatus(step, field);
	}

	/**
	 * Randomly populate the field with foxes and rabbits.
	 */
	private void populate() {
		Random rand = Randomizer.getRandom();
		field.clear();
		
		for (int row = 0; row < field.getDepth(); row++) {
			for (int col = 0; col < field.getWidth(); col++) {
				if (rand.nextDouble() <= BEAR_CREATION_PROBABILITY) {
					Location location = new Location(row, col);
					Bear bear = new Bear(true, field, location);
					actors.add(bear);
				} else if (rand.nextDouble() <= FOX_CREATION_PROBABILITY) {
					Location location = new Location(row, col);
					Fox fox = new Fox(true, field, location);
					actors.add(fox);
				} else if (rand.nextDouble() <= RABBIT_CREATION_PROBABILITY) {
					Location location = new Location(row, col);
					Rabbit rabbit = new Rabbit(true, field, location);
					actors.add(rabbit);
				} else if (rand.nextDouble() <= HUNTER_CREATION_PROBABILITY) {
					if (step == 0) {
						Location location = new Location(row, col);
						Actor actor = new Hunter(true, field, location);
						actors.add(actor);
					}
				}
				// else leave the location empty.
			}
		}
	}
}
